
package com.oahcfly.chgame.core.mvc;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
import java.util.Stack;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.viewport.StretchViewport;
import com.oahcfly.chgame.core.Chao;
import com.oahcfly.chgame.core.actions.CHScheduleAction;
import com.oahcfly.chgame.core.async.CHIOAsyncTask;
import com.oahcfly.chgame.core.event.CHEvent;
import com.oahcfly.chgame.core.event.CHEventListener;
import com.oahcfly.chgame.core.event.CHEventManager;
import com.oahcfly.chgame.core.helper.CHPixmapHelper;
import com.oahcfly.chgame.core.script.CHScript;
import com.oahcfly.chgame.core.ui.CHUI;
import com.oahcfly.chgame.egret.ui.EXMLBaseSkinUI;
import com.oahcfly.chgame.util.CHLog;

/** <pre>
 * 【View层】
 * 场景抽像类
 * 
 * 内部管理多个UI。UI可以自由切换，关闭和打开。
 * UI数据通过CHModel进行管理。
 * date: 2014-5-11
 * </pre>
 * 
 * @author caohao */
public abstract class CHScreen extends CHEventListener implements Screen, CHUIFocusListener {

	/** 初始化场景信息 */
	public abstract void initScreen ();

	/** 从当前场景切换到另一个场景需要前执行 */
	public abstract void endScreen ();

	/** 点击back键了 */
	public abstract void clickBackKey ();

	private String TAG = getClass().getSimpleName();

	// 舞台宽&高
	private int stageW = 0, stageH = 0;

	// CHUI的缓存
	private HashMap<String, CHUI> chuiMap;
	// EXMLBaseUI
	private HashMap<String, EXMLBaseSkinUI> baseSkinUIMap = new HashMap<String, EXMLBaseSkinUI>();

	private Stage stage;

	private CHModel chModel;

	// 影子
	private CHActor shadowActor;

	// chui堆栈
	private Stack<CHUI> chuiStack;

	// 是否已经处理了back键点击
	private boolean doBackClick = false;

	private Array<String> usedTextureArray;

	public CHScreen () {
		this(CHGame.getInstance().gameWidth, CHGame.getInstance().gameHeight);
	}

	public CHScreen (int stageW, int stageH) {
		this.stageW = stageW;
		this.stageH = stageH;
		chuiStack = new Stack<CHUI>();
		chuiMap = new HashMap<String, CHUI>();
	}

	@Override
	public void show () {
		Gdx.app.debug(TAG, "screen-show");
		setBackKeyPressed(false);
		createStage();
		initScreen();
	}

	private void createStage () {
		// 自动拉伸舞台
		StretchViewport viewport = new StretchViewport(stageW, stageH);
		stage = new Stage(viewport, getGame().getBatch());
		stage.getRoot().setName(getClass().getSimpleName());
		// 注册触摸事件
		Gdx.input.setInputProcessor(stage);

		shadowActor = CHActor.obtain();
		addActor(shadowActor);

		Gdx.app.debug(TAG, "screen-init-createStage");

		// 注册UI事件监听
		this.stage.getRoot().addListener(this);
		CHEventManager.getInstance().registerEvent(CHEventManager.EVENT_SCREEN_UI, this.stage.getRoot());
	}

	@Override
	public void hide () {
		Gdx.app.debug(TAG, "screen-hide");
		// screen销毁
		Set<String> keys = chuiMap.keySet();
		for (String key : keys) {
			// 依次关闭CHUI
			chuiMap.get(key).dismiss();
		}
		chuiMap.clear();

		chuiStack.clear();

		if (stage.getRoot() != null) {
			stage.getRoot().remove();
		}

		endScreen();

		if (usedTextureArray != null) {
			// 图片资源释放
			for (String texureName : usedTextureArray) {
				if (getGame().getAssetManager().isLoaded(texureName)) {
					getGame().getAssetManager().unload(texureName);
				}
			}
		}

		// 此处可能报错：java.lang.IllegalArgumentException: buffer not allocated with
		// newUnsafeByteBuffer or already disposed
		// 具体原因不清楚。。。。
		stage.dispose();

		// 清理纹理
		CHPixmapHelper.clearTextureCache();

		baseSkinUIMap.clear();
	}

	private boolean isPause;

	@Override
	public void pause () {
		// TODO Auto-generated method stub
		Gdx.app.debug(TAG, "screen-pause");
		isPause = true;
	}

	@Override
	public void render (float delta) {

		// 清屏
		Gdx.gl.glClearColor(0, 0, 0, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

		draw();
		act(delta);

		// 点击了返回键
		boolean pressBackKey = Gdx.input.isKeyJustPressed(Input.Keys.BACK);
		if (doBackClick && !pressBackKey) {
			// 已经通知过返回事件了，并且事件已经被处理完了，重置标志，继续监测
			doBackClick = false;
		}
		if (pressBackKey && !doBackClick && delta > 0) {
			// 监听返回键【未处理back键事件，且当前点击了】
			doBackClick = true;
			Gdx.app.postRunnable(new Runnable() {

				@Override
				public void run () {
					clickBackKey();
					doExmlUIBack();
				}
			});
			Gdx.app.debug("CHScreen", "[clickBackKey] :" + getClass().getSimpleName());
		}
	}

	protected void doExmlUIBack () {
		Collection<EXMLBaseSkinUI> baseSkinUIs = getBaseSkinUIMap().values();
		for (EXMLBaseSkinUI baseSkinUI : baseSkinUIs) {
			if (baseSkinUI != null && baseSkinUI.isShowing()) {
				baseSkinUI.clickBackKey();
			}
		}
	}

	public void draw () {
		getStage().draw();
	}

	public void act (float delta) {
		if (isPause) {
			return;
		}
		getStage().act();
	}

	@Override
	public void resize (int width, int height) {
		// TODO Auto-generated method stub
		Gdx.app.debug(TAG, "screen-resize");
	}

	@Override
	public void resume () {
		// TODO Auto-generated method stub
		Gdx.app.debug(getClass().getName(), "screen-resume");
		isPause = false;
	}

	public <T extends CHGame> T getGame () {
		return CHGame.getInstance();
	}

	public Stage getStage () {
		return stage;
	}

	public void addActor (Actor actor) {
		getStage().addActor(actor);
	}

	public void removeActor (CHActor actor) {
		this.stage.getRoot().removeActor(actor);
	}

	public <T extends Actor> T findActor (String name) {
		return getStage().getRoot().findActor(name);
	}

	public String getTAG () {
		return TAG;
	}

	private void setBackKeyPressed (boolean backKey) {
		this.doBackClick = backKey;
	}

	@SuppressWarnings("unchecked")
	public <T extends CHModel> T getModel () {
		return (T)chModel;
	}

	public void setModel (CHModel chModel) {
		this.chModel = chModel;
		if (chModel.getScreen() == null) chModel.setScreen(this);
	}

	public void addUI (CHUI chui) {
		chuiMap.put(chui.getClass().getSimpleName(), chui);
	}

	public void showUI (String uiname) {
		getUI(uiname).show();
	}

	/** <pre>
	 *  chui会关闭显示
	 * 
	 * date: 2015-4-11
	 * </pre>
	 * 
	 * @author caohao
	 * @param chui
	 * @param newUIName */
	public void changeUI (CHUI chui, String newUIName) {
		chui.dismiss();
		getUI(newUIName).show();
	}

	/** <pre>
	 * 根据名称得到CHUI
	 * 
	 * date: 2015-1-12
	 * </pre>
	 * 
	 * @author caohao
	 * @param name
	 * @return */
	@SuppressWarnings("unchecked")
	public <T extends CHUI> T getUI (String name) {
		return (T)chuiMap.get(name);
	}

	public void dispose () {
	}

	@Override
	public void notifyUIUnFocus (CHUI chui) {
		// chui关闭了。从栈里面移除。
		chuiStack.remove(chui);
	}

	@Override
	public void notifyUIFocus (CHUI chui) {
		// chui显示。加入栈内，处在顶部
		chuiStack.push(chui);
	}

	/** <pre>
	 * 当前正在展示的CHUI
	 * 
	 * date: 2015-1-12
	 * </pre>
	 * 
	 * @author caohao
	 * @return */
	public CHUI getTopCHUI () {
		if (chuiStack.isEmpty()) return null;
		return chuiStack.peek();
	}

	/** <pre>
	 * 加入异步任务处理
	 * 
	 * date: 2015-1-17
	 * </pre>
	 * 
	 * @author caohao
	 * @param asyncTask */
	public void addAsyncTask (CHIOAsyncTask asyncTask) {
		CHGame.getInstance().getAsyncManager().loadTask(asyncTask);
	}

	/** <pre>
	 * 时间调度[同步，在绘制线程中执行，方法内不能有耗时处理]
	 * 【每间隔duration时间，调用methodName的方法】
	 * date: 2015-1-22
	 * </pre>
	 * 
	 * @author caohao
	 * @param methodName 方法名
	 * @param duration 间隔时间 */
	public void addSyncSchedule (String methodName, float duration) {
		try {
			final Class<?> screenClass = this.getClass();
			final Method method = screenClass.getDeclaredMethod(methodName);

			Action runnableAction = Actions.run(new Runnable() {

				@Override
				public void run () {
					try {
						method.invoke(CHScreen.this);
					} catch (IllegalAccessException e) {
						// TODO Auto-generated catch block
						Gdx.app.error(TAG, "addSyncSchedule :" + e.getMessage());
					} catch (IllegalArgumentException e) {
						// TODO Auto-generated catch block
						Gdx.app.error(TAG, "addSyncSchedule :" + e.getMessage());
					} catch (InvocationTargetException e) {
						// TODO Auto-generated catch block
						Gdx.app.error(TAG, "addSyncSchedule :" + e.getMessage());
					}
				}
			});
			for (Action action : shadowActor.getActions()) {
				if (action instanceof CHScheduleAction && ((CHScheduleAction)action).getName().equals(methodName)) {
					// 发现已有action,重复利用，修改间隔时间即可
					((CHScheduleAction)action).setDuration(duration);
					action.restart();
					return;
				}
			}

			shadowActor.addAction(new CHScheduleAction(methodName, duration, -1, runnableAction));
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			Gdx.app.error(TAG, "addSyncSchedule :" + e.getMessage());
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			Gdx.app.error(TAG, "addSyncSchedule :" + e.getMessage());
		}
	}

	/** <pre>
	 * 取消时间调度[同步]
	 * 
	 * date: 2015-1-22
	 * </pre>
	 * 
	 * @author caohao
	 * @param methodName 方法名 */
	public void unSyncSchedule (String methodName) {
		for (Action action : shadowActor.getActions()) {
			if (action instanceof CHScheduleAction) {
				// 直接完成
				((CHScheduleAction)action).finish();
			}
		}
	}

	public boolean isBackKey () {
		return doBackClick;
	}

	public InputProcessor getInputProcessor () {
		// TODO Auto-generated method stub
		return stage;
	}

	@Override
	public void handleEvent (CHEvent chEvent) {

		EXMLBaseSkinUI baseSkinUI = baseSkinUIMap.get(chEvent.getUIData().targetActorName);
		if (baseSkinUI != null) {
			// 直接处理掉了
			Action action = chEvent.getUIData().getAction();
			if (action != null) {
				baseSkinUI.show(action);
			} else {
				baseSkinUI.show();
			}
			baseSkinUI.handleUIEvent(chEvent.getUIData().dataMap);
			return;
		}

		this.handleUIEvent(chEvent.getUIData().targetActorName, chEvent.getUIData().dataMap);

	}

	/** 处理UI事件
	 * 
	 * @param event */
	public abstract void handleUIEvent (String key, HashMap<String, String> data);

	/** 在延迟duration秒后执行某个操作
	 * 
	 * @param runnable
	 * @param duration */
	public void setTimeout (Runnable runnable, float duration) {
		this.shadowActor.addAction(Actions.delay(duration, Actions.run(runnable)));
	}

	public void showSkinUI (final EXMLBaseSkinUI skinUI, Action action) {
		skinUI.setTouchable(Touchable.disabled);
		if (action != null) {
			skinUI.addAction(Actions.sequence(action, Actions.run(new Runnable() {

				@Override
				public void run () {
					skinUI.setTouchable(Touchable.enabled);
				}
			})));
		} else {
			skinUI.setTouchable(Touchable.enabled);
		}
		addActor(skinUI);

	}

	public void dismissSkinUI (EXMLBaseSkinUI skinUI, Action action) {
		skinUI.setTouchable(Touchable.disabled);
		if (action != null) {
			skinUI.addAction(Actions.sequence(action, Actions.removeActor()));
		} else {
			skinUI.remove();
		}
	}

	public void showSkinUI (final EXMLBaseSkinUI skinUI) {
		showSkinUI(skinUI, Actions.moveTo(0, 0, 0.3f, Interpolation.bounceOut));
		skinUI.setX(-skinUI.getWidth());
	}

	public void dismissSkinUI (EXMLBaseSkinUI skinUI) {
		dismissSkinUI(skinUI, Actions.sequence(Actions.moveTo(Chao.gameW, 0, 0.3f), Actions.moveTo(0, 0)));
	}

	public Array<String> getUsedTextureArray () {
		return usedTextureArray;
	}

	public void setUsedTextureArray (Array<String> usedTextureArray) {
		this.usedTextureArray = usedTextureArray;
	}

	/** 创建脚步控制器
	 * 
	 * @return */
	public CHScript buildScript () {
		return new CHScript(this);
	}

	public Image addImage (String path) {
		Image image = Chao.game.getImage(path);
		addActor(image);
		image.setOrigin(Align.center);
		return image;
	}

	public void addExmlBaseSkinUI (EXMLBaseSkinUI exmlBaseSkinUI) {
		baseSkinUIMap.put(exmlBaseSkinUI.getCMDName(), exmlBaseSkinUI);
	}

	public void addExmlBaseSkinUI (Class<?> uiclass) {
		EXMLBaseSkinUI exmlBaseSkinUI = null;
		try {
			exmlBaseSkinUI = (EXMLBaseSkinUI)Class.forName(uiclass.getName()).newInstance();
		} catch (Exception e) {
			CHLog.error(e.getLocalizedMessage());
			return;
		}
		baseSkinUIMap.put(exmlBaseSkinUI.getCMDName(), exmlBaseSkinUI);
	}

	public HashMap<String, EXMLBaseSkinUI> getBaseSkinUIMap () {
		return baseSkinUIMap;
	}

}

interface CHUIFocusListener {
	/** <pre>
	 * chui不显示，失去焦点
	 * 
	 * date: 2015-1-12
	 * </pre>
	 * 
	 * @author caohao
	 * @param chui */
	public void notifyUIUnFocus (CHUI chui);

	/** <pre>
	 * chui显示，获得焦点
	 * 
	 * date: 2015-1-12
	 * </pre>
	 * 
	 * @author caohao
	 * @param chui */
	public void notifyUIFocus (CHUI chui);

}
